Skip to content

Name the Ad Manager decision in the auction trace console - #975

Draft
prk-Jr wants to merge 1 commit into
feature/auction-creative-trace-overlayfrom
fix/ad-trace-gam-attribution
Draft

Name the Ad Manager decision in the auction trace console#975
prk-Jr wants to merge 1 commit into
feature/auction-creative-trace-overlayfrom
fix/ad-trace-gam-attribution

Conversation

@prk-Jr

@prk-Jr prk-Jr commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stacked on #961 — base branch is feature/auction-creative-trace-overlay, so review this diff on its own and let GitHub retarget the base to main once #961 lands.

The trace console could not answer the question it exists to answer: after the GAM call, did the line item Trusted Server won render, or did a house ad, a direct-sold line item, or the Ad Manager default render instead? Every non-empty, non-backfill render read as direct_or_unattributed — "GAM rendered an ad — source not attributed".

Three causes, all in the GAM stage:

  1. slotRenderEnded identifiers were read as booleans and discarded. lineItemId and creativeId were only tested for presence; advertiserId, campaignId, sourceAgnosticLineItemId, sourceAgnosticCreativeId, yieldGroupIds, and companyIds were not read at all. Ad Manager's own report is the only authoritative statement of which line item won.
  2. The computed response class never reached a stage outcome, so reservation and unclassified_non_empty both fell into the single unattributed bucket.
  3. Attribution required the creative's cooperative acknowledgement. A markup request from the rendered creative — which only happens when Ad Manager selected the header-bidding line item — did not settle the GAM stage, so a suppressed, sandboxed, or slow creative was indistinguishable from losing the Ad Manager decision, and a slot with live Trusted Server targeting stayed a trusted_server_candidate indefinitely.

Changes

File Change
crates/trusted-server-js/lib/src/integrations/gpt/index.ts Read the full slotRenderEnded identifier set with bounded validation; arm a 5s attribution window for a render on a generation that had a Trusted Server bid; settle it when the rendered creative requests markup.
crates/trusted-server-js/lib/src/core/ad_trace.ts Re-sanitize the identifiers into generation diagnostics; add other_reservation, gam_default_or_unclassified, and other_gam_demand outcomes; credit trusted_server_won at strong confidence on a markup request or served markup; stop later GPT callbacks from downgrading a settled win.
crates/trusted-server-js/lib/src/core/types.ts Add AdTraceGamIdentity and the gpt_render_unclaimed event kind.
crates/trusted-server-js/lib/src/integrations/ad_trace/presentation.ts Name each Ad Manager outcome, surface the reported identifiers as an operator fact, and prefer a named decision over the generic unattributed row.
crates/trusted-server-integration-tests/browser/tests/ad-trace/auction-trace.spec.ts Update the two expectations that encoded the old ambiguity: a suppressed creative now resolves to other_gam_demand, and the cleared-targeting render asserts other_reservation plus the reported line item ID.
docs/guide/configuration.md Document what the console reports for a render Trusted Server cannot claim, and that the identifiers stay in the browser.

Behavior

The console now reports one of: Trusted Server creative selected (confirmed, or selected with the confirmation still outstanding), another reservation line item plus its Ad Manager identifiers, an Ad Manager default or backup ad, other Ad Manager demand where the Trusted Server creative never ran, backfill, or empty.

Ad Manager identifiers are the publisher's own data — the same values ?google_console=1 shows — and are gated behind the existing console session. They are not added to auction telemetry.

Distinguishing a house line item from a direct-sold one still requires looking the ID up in Ad Manager. Auto-labeling that needs a configured allowlist of header-bidding and house order IDs, deliberately left as a follow-up rather than guessed at here.

Test plan

  • JS tests: cd crates/trusted-server-js/lib && npx vitest run (531 passed, 12 new)
  • JS format: cd crates/trusted-server-js/lib && npm run format
  • JS build: cd crates/trusted-server-js/lib && node build-all.mjs
  • Docs format: cd docs && npm run format
  • Playwright ad-trace suite — needs the docker fixture and viceroy; the two updated expectations are unverified locally and need a CI run
  • Manual verification on the test site with ?ts_console=true, checking a slot where GAM delivers a house ad against one where the Trusted Server creative renders

Checklist

  • Changes follow CLAUDE.md conventions
  • No secrets, real domains, or real operator configuration values
  • New code has tests

The GAM stage collapsed every non-empty, non-backfill render into a single
unattributed outcome, so a house ad, a direct-sold line item, an Ad Manager
default, and a Trusted Server win whose creative never confirmed all read
identically. Attribution also depended solely on the creative's cooperative
acknowledgement, leaving a slot with live Trusted Server targeting unresolved
forever when that acknowledgement never arrived.

Retain the identifiers slotRenderEnded reports for the delivered ad and
classify the render from them, credit the Ad Manager decision as soon as the
rendered creative requests markup, and resolve a render the Trusted Server
creative never claimed within a bounded window. Ad Manager identifiers stay
in the browser and are never sent to auction telemetry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant